package com.wnxy.portal.anno;

import cn.hutool.core.codec.Base64;
import cn.hutool.json.JSONUtil;
import com.wnxy.portal.common.exception.BusinessException;
import com.wnxy.portal.common.result.impl.BusinessCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

/**
 * 切面类，拦截@Idempotent注解，做幂等校验
 */
@Component
@Aspect // 指定当前类为切面类，需要引入：spring-boot-starter-aop 依赖
@Slf4j
public class IdempotentAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 环绕通知：环绕目标方法执行，可以在方法执行前、执行后做一些业务处理
     */
    @Around("@annotation(idempotent)")
    public Object handler(ProceedingJoinPoint pjp,Idempotent idempotent) throws Throwable {
        // 获取方法参数
        Object[] args = pjp.getArgs();
        // 相同参数的请求只能执行一次
        String paramsString = JSONUtil.toJsonStr(args);
        log.debug("请求参数json，{}",paramsString);
        // 对字符串进行Base64加密
        String key = Base64.encode(paramsString);
        log.debug("请求参数Base64编码：{}",key);
        // 幂等校验
        if (!redisTemplate.opsForValue().setIfAbsent(key,"1",idempotent.expireTime(),idempotent.timeUnit())) {
            throw new BusinessException(BusinessCodeEnum.ORDER_REPEAT_COMMIT);
        }

        // 校验通过：放行，执行被拦截的方法(如：控制器方法)
        Object proceed = pjp.proceed();
        return proceed;
    }

}
